#include "createREF.h"
#include "legoland.h"
#include "frequency.h"
#include "kmeans.h"
#include <unistd.h>
#define GetCurrentDir _getcwd
#include <iostream>
#include <fstream>
#include <cctype>
#include <cstring>
#include <cstdlib>
#include <cmath>

using namespace std;

bool file_exists(char * file_name);
bool correct_format(char * file_name);

/*************************************************************************
* CreateREF - constructor
*   Sets up initial parameter values. Makes calls to retieve all
*   parameters and verifies that files exist.
**************************************************************************/
CreateREF::CreateREF()
{
    system("CLS");
    cout    << "Create a Reference Set for DISCO\n\n";

    gc_dim1 = 25;
    gc_dim2 = 75;

    char *path = NULL;
    path = getcwd(NULL, 0); // or _getcwd
    if ( path != NULL)
        printf("%s\n", path);


    gc_dim1_file=new char [500];
    gc_dim2_file=new char [500];
    sprintf(gc_dim1_file,"%s/GC_axis/GC%d%s", path, gc_dim1, ".ref");
    sprintf(gc_dim2_file,"%s/GC_axis/GC%d%s", path, gc_dim2, ".ref");

    cout << gc_dim1_file << endl;

    if(get_parameters())
    {
        if(!load_taxa())
        {
            cout << "There is a problem with the reference set file. Please check this file and the DISCO documentation.\n";
            cout << "The library was not created." << endl;
        }
        else
        {
            if(!create_reference_library())
            {
                cout << "An error occurred during the creation of the library.\n";
                cout << "Please verify that the reference set file is correct.\n";
                cout << "The library was not created." << endl;
            }
            cout << "The reference library " << output_file_name << " was created." << endl;
        }
    }
    else
        cout << "The parameters were not set correctly. No library was created." << endl;
}

/*************************************************************************
* CreateREF - deconstructor
*   deletes all dynamically allocated memory class variables
**************************************************************************/
CreateREF::~CreateREF()
{
    int i;
    delete reference_file;
    delete output_file_name;
    for(i=0; i<num_gs; i++)
        delete taxa[i];
    delete taxa;
    delete gc_dim1_file;
    delete gc_dim2_file;
}

/*************************************************************************
* create_reference_library
*   Performs frequency k-mer counts, reduces to axes, clusters,
*   and writes out results. l is instantiated in this function
**************************************************************************/
bool CreateREF::create_reference_library()
{
    int i,j;
    int num_coordinates;
    int multiplier;
    coordinates * c;
    kmeans * clusters;
    centroid * plot;
    legoland ** l=new legoland * [k_max-k_min+1];
    int kum=read_size;
    Profile * p;

    if(!use_default_LLmult)
    {
        cout << "Please enter the mapping multiplier you wish to use. Refer to documentation for\n";
        cout << "further information regaurding this variable. Value: ";
        cin >> multiplier;
        if(multiplier<=0) {cout << "This is not a valid value. The default will be used." << endl; use_default_LLmult=true;}
    }

    for(i=k_min; i<=k_max; i++)
    {
        p=new Profile(i,read_size,gc_dim1_file,gc_dim2_file);

        if(!use_default_LLmult) l[i]=new legoland(multiplier);
        else l[i]=new legoland();


        for(j=0; j<num_gs; j++)
        {
            c=NULL;
            num_coordinates=p->get_coordinates_genome(reference_file,j,c);
            if(num_coordinates==0)
            {
                delete c;
                j=num_gs;
                i=k_max;
            }
            clusters=new kmeans(num_coordinates, c, kum);
            delete c;

            plot=NULL;
            clusters->execute(plot);
            l[i]->add(j,plot,kum);                      //map to legoland
            delete plot;
            delete clusters;
        }
        delete p;
    }

    if(num_coordinates==0)
    {
        for(i=0; i<k_max-k_min+1; i++)
            delete l[i];
        delete l;
        return false;
    }

    //write out to file
    ofstream out;
    char file[300];
    sprintf(file,"%s_disco.lib",output_file_name);
    out << k_min << "\t" << k_max << endl;
    out << gc_dim1 << "\t" << gc_dim2 << endl;
    out << read_size << endl << l[i]->get_multiplier() << endl;
    out << num_gs << endl;
    for(i=0; i<num_gs-1; i++)
        out << taxa[i] << "\t";
    out << taxa[i] << endl;
    out << "*" << endl;
    vector<lego> v;
    unsigned long long ii;
    for(i=k_min; i<=k_max; i++)
    {
        for(ii=0; ii<l[i]->get_size_legoville(); ii++)
        {
            v=l[i]->search(ii);
            if(v.size()!=0)
            {
                out << ii << "\t" << v.size() << "\t";
                for(j=0; j<v.size()-1; j++)
                    out << v[j].taxon << "\t" << v[j].prob << "\t";
                out << v[j].taxon << "\t" << v[j].prob << endl;
            }
        }
        out << "*";
        delete l[i];            //cleaning memory as i write
    }
    out.clear();
    out.close();


    //clean up memory
    delete l;

    return true;
}

/*************************************************************************
* get_parameters
*   Asks user for the basic parameters
**************************************************************************/
bool CreateREF::get_parameters()
{
    char selection;
    bool get_ks=true;

    if(!file_exists(gc_dim1_file) || !file_exists(gc_dim2_file))
    {
        cout << "Please check that the corresponding files are in the folder\nlabeled GC_axis\n";
        return false;
    }
    cout << "Got to get_parameters" << endl;

    if(get_reference_file())
    {
        while(get_ks)
        {
            cout<<"Enter new min k-mer size: ";
            cin>>k_min;
            cout<<"Enter new max k-mer size (Maximum 9): ";
            cin>>k_max;
            if(k_max>9) k_max=9;
            if(k_max>=k_min) get_ks=false;
            else
            {
                cout << "The k-mer minimum size must be less than the k-mer maximum size." << endl;
                cout << "Try again.\n\n";
            }
        }

        output_file_name=new char [300];
        cout << "Enter the name for the library file to be written (no file extension):\n";
        cin >> output_file_name;

        cout << "Do you wish to set the advanced parameters? ";
        cin>> selection;
        if(selection=='y' || selection=='Y')
        {
            if(get_advanced_parameters())
                return true;
        }
        else return true;
    }
    return false;
}

/*************************************************************************
* get_advanced_parameters
*   Gets the advanced parameters including:
*       - the mapping multiplier
*       - the axes
*   returns true unless the GC files are missing
**************************************************************************/
bool CreateREF::get_advanced_parameters()
{
    //changes GC content references
    char selection;
    int temp_gc_dim1, temp_gc_dim2;
    cout << endl;
    cout    << "Advanced Parameters: \n\n";

    cout << "Do you wish to use the default mapping multiplier? (Y-recommended or N) : ";
    cin>>selection;
    if(selection=='N') use_default_LLmult=false;

    cout << endl;
    cout << "Read compositions will be mapped to synthetic genomes of GC content ";
    cout << gc_dim1 << " & " << gc_dim2 << "." << endl << endl;
    cout << "Do you wish to change these? (Y/N) ";
    cin>>selection;
    if(selection>96) selection-=32;

    if(selection=='Y')
    {
        cout << "Axes can be of a GC between 25 and 75%, multiples of 5.\n";
        cout << "New GC content axis 1: ";
        cin >> temp_gc_dim1;
        while( (temp_gc_dim1%5!=0) && (temp_gc_dim1>=25 && temp_gc_dim1<=75) )
        {
            cout << "Axes can be of a GC between 25 and 75%, multiples of 5.\n";
            cout << "New GC content axis 1: ";
            cin >> temp_gc_dim1;
        }
        cout << "New GC content axis 2: ";
        cin >> temp_gc_dim2;
        while( (temp_gc_dim2%5!=0) && (temp_gc_dim2>=25 && temp_gc_dim2<=75) )
        {
            cout << "Axes can be of a GC between 25 and 75%, multiples of 5.\n";
            cout << "New GC content axis 2: ";
            cin >> temp_gc_dim2;
        }
        cout << "Do you wish to make these changes? (Y/N) ";
        cin>>selection;
        if(selection>96) selection-=32;
        if(selection=='Y')
        {
            gc_dim1=temp_gc_dim1;
            gc_dim2=temp_gc_dim2;
            sprintf(gc_dim1_file,"~/GC_axis/%d.ref",gc_dim1);
            sprintf(gc_dim2_file,"~/GC_axis/%d.ref",gc_dim2);
            if(!file_exists(gc_dim1_file) || !file_exists(gc_dim2_file))
            {
                cout << "Please check that the corresponding files are in the folder\nlabeled GC_axes\n";
                return false;
            }
        }
    }
    else
    {
        cout << "No changes will be made." << endl;
    }
    return true;
}

/*************************************************************************
* correct_format
*   Prompts user for the reference file. User can quit (return false).
**************************************************************************/
bool CreateREF::get_reference_file()
{
    cout << "Please refer to documentation re: the formatting of reference set files." << endl;
    cout << "Please enter the file listing the genomes to be included in the reference:\n";

    char *path = NULL;
    path = getcwd(NULL, 0); // or _getcwd

    char *user_ref_file=new char [300];
    cin >> user_ref_file;
    strcat(path,user_ref_file);
    reference_file = path;

    cout << "Please enter the length of the read: " << endl;
    cin >> read_size;

    cout << "Looking for: " << reference_file << endl;
    if(!file_exists(reference_file))            //Maybe use a while loop here? while(!file_exists(reference_file)
    {
        cout << "This file does not exist. Please try again.\n";
        cout << "Please enter the file listing the genomes to be included in the reference or type Q to exit:\n";
        cin >> user_ref_file;
        strcat(path,user_ref_file);
        reference_file = path;
        if( (reference_file[0]=='Q' || reference_file[0]=='q') && (strlen(reference_file)==1) )
        {
            cout << "Goodbye" << endl;
            return false;
        }
    }
    cout << "About to check format of reference file" << endl;
    if(correct_format(reference_file))
        return true;
    else
        return false;
}

/*************************************************************************
* load_taxa
*   Retrieves taxa from user format file. Loads the taxa into the taxa
*   table and assigns class variable num_gs.
*   return true if loaded, false if there is a problem with the file
**************************************************************************/
bool CreateREF::load_taxa()
{
    if(taxa!=NULL)
        delete taxa;
    int counter;
    char line[1000];
    ifstream in;
    in.open(reference_file);
    cout << "Ref file: " << reference_file << endl;
    num_files_reference=0;
    num_gs=0;
    while(in.peek()!=EOF)
    {
        in.getline(line,1000,'\t');        //strips of genus species or -
        if(strcmp(line,"-")!=0)
            num_gs++;
            cout << "taxa: " << line << endl;
        in.getline(line,1000,'\n');
        num_files_reference++;
    }
    in.clear();
    in.close();
    if(num_gs==0)
    {
        cout << "The reference file is not formatted properly. Please refer to documentation." << endl;
        return false;
    }

    counter=0;
    taxa=new char * [num_gs];
    in.open(reference_file);
    while(in.peek()!=EOF)
    {
        in.getline(line,1000,'\t');        //genus/species if r or - if alt
        if(strcmp(line,"-")!=0)
        {
            taxa[counter]=new char [strlen(line)+1];
            strcpy(taxa[counter],line);
            counter++;
        }
        in.getline(line,1000,'\n');

    }
    in.clear();
    in.close();
    return true;
}

///////////////////////////// LOCAL VARIABLES /////////////////////////////

/*************************************************************************
* correct_format
*   parameters:
*       -file_name:     file name of the taxa/genome file list
*   returns true if in the right format, else false
**************************************************************************/
bool correct_format(char * file_name)
{
    char line[500];
    ifstream in;
    in.open(file_name);
    if(!in.is_open())
    {
        cout << "file not open" << endl;
        return false;}
    cout << "Found File for formatting" << endl;
    in.getline(line,500,'\t');
    cout << "First line grabbed: " << line << endl;
    if(strchr(line,10)!=NULL)
    {
        cout << "first check" << endl;
        in.clear();
        in.close();
        cout << "failure 1" << endl;
        return false;
    }

    in.getline(line,500,'\t');
    cout << "Second line grabbed: " << line << endl;
    if(strchr(line,10)!=NULL)
    {
        in.clear();
        in.close();
        cout <<"Failure 2" << endl;
        return false;
    }

    in.getline(line,500,'\t');
    cout << "Third line grabbed: " << line << endl;
    if(strchr(line,10)!=NULL)
    {
        in.clear();
        in.close();
        cout <<"Failure 3" << endl;
        return false;
    }

    in.getline(line,500,'\n');
    cout << "Fourth line grabbed: " << line << endl;
    if(strchr(line,10)!=NULL)
    {
        in.clear();
        in.close();
        cout <<"Failure 4" << endl;
        return false;
    }
    in.clear();
    in.close();
    cout << "Finished checking format and its correct" << endl;
    return true;
}

/*************************************************************************
* file_exist - determines if a file exists
*   parameters:
*       -file_name: name of file you are checking if it exists
*   return: true if file exists else false
**************************************************************************/
bool file_exists(char * file_name)
{
    ifstream in;
    in.open(file_name);
    if(in.is_open())
    {
        in.clear();
        in.close();
        return true;
    }
    else
        return false;
}


